﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Text;
using Dry.DbInterpreter;
using System.IO;
using Dry.General;
using Microsoft.Practices.EnterpriseLibrary.Data;

namespace Dry.Generators
{
    public class BOGenerator : GeneratorBase
    {
        private static string _tablePrefix;
        private static bool _localization;

        public static GeneratorFeedback CreateFile(string strTableName, bool localization)
        {
            return CreateFile(strTableName, localization, "tbl");
        }

        public static GeneratorFeedback CreateFile(string strTableName, bool localization, string tablePrefix)
        {
            GeneratorFeedback generatorFeedback = new GeneratorFeedback();

            _tablePrefix = tablePrefix;
            _localization = localization;

            string strCode = GetBOClass(strTableName, localization);
            generatorFeedback.Exception = CreateFileFromString("BO\\AutoGenerated\\" + strTableName.Substring(tablePrefix.Length, strTableName.Length - tablePrefix.Length) + "BO.cs", strCode);
            generatorFeedback.Lines = CountLinesInString(strCode);

            return generatorFeedback;
        }

        public static void CreateBaseBO()
        {
            string strCode = Templates.BaseBO.GetBaseBO().Replace("¤ProjectName¤", Dry.ProjectName);
            CreateFileFromString("BO\\BaseBO.cs", strCode);
        }

        public static string GetBOClass(string strTableName, bool localization)
        {
            string strCode = String.Empty;
            string strPK = string.Empty;
            DbTable dbTable = new DbTable(strTableName, _tablePrefix);

            strCode += GetFileHeader(Dry.ProjectName, "", "", _localization.ToString(), _tablePrefix, "");

            // Declare the BO class
            strCode += "using System;" + _strNewLine;
            strCode += "using System.Collections.Generic;" + _strNewLine;
            strCode += "using " + Dry.ProjectName + ".BLL;" + _strNewLine;
            strCode += "using " + Dry.ProjectName + ".IP;" + _strNewLine;
            strCode += "using " + Dry.ProjectName + ".DAL;" + _strNewLine;
            strCode += "" + _strNewLine;
            strCode += "namespace " + Dry.ProjectName + ".BO" + _strNewLine;
            strCode += "{" + _strNewLine;
            strCode += "    [Serializable]" + _strNewLine;
            strCode += "    public partial class " + dbTable.ClassName + "BO : BaseBO" + _strNewLine;
            strCode += "    {" + _strNewLine;

            // Declarations
            foreach (DbColumn dbColumn in dbTable.DbColumns)
            {
                if (dbColumn.ColumnName == "Created" || dbColumn.ColumnName == "Modified")
                    continue;

                strCode += "        public " + GetCodeTypeFromDbType(dbColumn.DataType);
                if (dbColumn.IsNullable && GetCodeTypeFromDbType(dbColumn.DataType) != "string" && GetCodeTypeFromDbType(dbColumn.DataType) != "Byte[]")
                    strCode += "?";
                strCode += " " + dbColumn.ColumnName + " { get; set; }" + _strNewLine;

                if(dbColumn.IsPK)
                    strPK = dbColumn.ColumnName;

                if (dbColumn.IsFK)
                {
                    string strFkObjectName = "_" + dbColumn.ColumnName.Substring(0, 1).ToLower() + dbColumn.ColumnName.Substring(1, dbColumn.ColumnName.Length - 1);

                    strCode += "        protected " + dbColumn.ReferenceClass;
                    strCode += "BO " + strFkObjectName + ";" + _strNewLine;

                    strCode += "        public " + dbColumn.ReferenceClass;
                    strCode += "BO " + dbColumn.ColumnName.Substring(0, dbColumn.ColumnName.Length - 2) + _strNewLine;
                    strCode += "        {" + _strNewLine;
                    strCode += "            get" + _strNewLine;
                    strCode += "            {" + _strNewLine;
                    if (dbColumn.IsNullable)
                    {
                        strCode += "                if(" + dbColumn.ColumnName + ".HasValue)" + _strNewLine;
                        strCode += "                    if(" + strFkObjectName + " != null && " + strFkObjectName + "." + dbColumn.ReferenceClass + "Id == " + dbColumn.ColumnName + ".Value)" + _strNewLine;
                        strCode += "                        return " + strFkObjectName + ";" + _strNewLine;
                        strCode += "                    else" + _strNewLine;
                        strCode += "                    {" + _strNewLine;
                        strCode += "                        " + strFkObjectName + " = " + dbColumn.ReferenceClass + "BLL.GetById(" + dbColumn.ColumnName.Substring(0, dbColumn.ColumnName.Length - 2) + "Id.Value);" + _strNewLine;
                        strCode += "                        return " + strFkObjectName + ";" + _strNewLine;
                        strCode += "                    }" + _strNewLine;
                        strCode += "                else" + _strNewLine;
                        strCode += "                    return null;" + _strNewLine;
                    }
                    else
                    {
                        strCode += "                if (" + strFkObjectName + " == null || " + strFkObjectName + "." + dbColumn.ReferenceClass + "Id != " + dbColumn.ColumnName + ")" + _strNewLine;
                        strCode += "                    " + strFkObjectName + " = " + dbColumn.ReferenceClass + "BLL.GetById(" + dbColumn.ColumnName + ");" + _strNewLine;
                        strCode += "                return " + strFkObjectName + ";" + _strNewLine;
                    }
                    strCode += "            }" + _strNewLine;
                    strCode += "        }" + _strNewLine;
                }
            }

            strCode += "        public " + dbTable.ClassName + "BO()" +_strNewLine;
            strCode += "        {" + _strNewLine;

            foreach (DbColumn dbColumn in dbTable.DbColumns)
            {
                if (dbColumn.ColumnName == "Password")
                {
                    int foo = 42;
                }

                if (dbColumn.DataType == "nvarchar" || dbColumn.DataType == "nchar")
                    strCode += "            " + dbColumn.ColumnName + " = \"\";" + _strNewLine;
                if (dbColumn.DataType == "uniqueidentifier")
                    strCode += "            " + dbColumn.ColumnName + " = System.Guid.NewGuid();" + _strNewLine;
                if (dbColumn.DataType == "binary")
                    strCode += "            " + dbColumn.ColumnName + " = new byte[64];" + _strNewLine;
            }
            strCode += "        }" + _strNewLine;

            strCode += GetBORelations(dbTable.TableName, strPK, localization);

            //strCode += GetBOIComparable(dbTable);


            strCode += "    }" + _strNewLine;
            strCode += "}";

            return strCode;
        }

        private static string GetBOIComparable(DbTable dbTable)
        {
            //This method doesnt quite work, fix and implement later (also, add inheritance to IComparable when adding this)

            StringBuilder strIComparable = new StringBuilder();

            strIComparable.AppendLine("            #region IComparable Members");
            strIComparable.AppendLine("");
            strIComparable.AppendLine("            public int CompareTo(object obj)");
            strIComparable.AppendLine("            {");
            strIComparable.AppendLine("                if (obj == null) return 1;");
            strIComparable.AppendLine("                ChurchInvoiceBO otherChurchInvoiceBO = obj as ChurchInvoiceBO;");
            strIComparable.AppendLine("                if (otherChurchInvoiceBO != null)");
            strIComparable.AppendLine("                    return ");

            foreach (DbColumn dbColumn in dbTable.DbColumns)
            {
                if(true)
                    strIComparable.AppendLine("                           this." + dbColumn.ColumnName + ".CompareTo(otherChurchInvoiceBO." + dbColumn.ColumnName + ") &&");
                else
                    strIComparable.AppendLine("                           this." + dbColumn.ColumnName + ".HasValue.CompareTo(otherChurchInvoiceBO." + dbColumn.ColumnName + ") &&");
            }
            strIComparable.Remove(strIComparable.Length - 4, 2);
            strIComparable.AppendLine("                    ;");

            strIComparable.AppendLine("                else");
            strIComparable.AppendLine("                    throw new ArgumentException(\"Object is not a ChurchInvoiceBO\");");
            strIComparable.AppendLine("            }");
            strIComparable.AppendLine("");
            strIComparable.AppendLine("            #endregion");

            return strIComparable.ToString();
        }

        private static string GetBORelations(string tableName, string strPK, bool localization)
        {
            StringBuilder strRelations = new StringBuilder();
            string strObjectName = string.Empty;


            Database db = GetDatabase();
            DbCommand dbCommand = db.GetSqlStringCommand("exec sp_fkeys @pktable_name = '" + tableName + "'");
            
            DataSet dataSet = db.ExecuteDataSet(dbCommand);
            
            foreach (DataRow dataRow in dataSet.Tables[0].Rows)
            {
                strObjectName = dataRow["FKTABLE_NAME"].ToString().TrimStart(_tablePrefix.ToCharArray());
                
                strRelations.AppendLine(" ");
                strRelations.AppendLine("        public List<" + strObjectName + "BO> ListOf" + strObjectName + "BO" + "On" + dataRow["FKCOLUMN_NAME"]);
                strRelations.AppendLine("        {");
                strRelations.AppendLine("            get");
                strRelations.AppendLine("            {");
                strRelations.AppendLine("                " + strObjectName + "IP " + strObjectName.Substring(0, 1).ToLower() + strObjectName.Substring(1) + "IP = new " + strObjectName + "IP{" + dataRow["FKCOLUMN_NAME"] + " = " + strPK + "};");
                strRelations.AppendLine("                return " + strObjectName + "BLL.GetListByIP(" + strObjectName.Substring(0,1).ToLower() + strObjectName.Substring(1) + "IP);");
                strRelations.AppendLine("            }");
                strRelations.AppendLine("        }");

                if(localization)
                {
                    // If the relation ends with Loc and is not the tblLoc table
                    if (strObjectName.Substring(strObjectName.Length - _tablePrefix.Length) == "Loc" && strPK != "LocId")
                   {
                        strRelations.AppendLine(" ");
                        strRelations.AppendLine("        public " + strObjectName + "BO GetLocalized(int locId)");
                        strRelations.AppendLine("        {");
                        strRelations.AppendLine("           // Try to get localized info about the object form the given locId");
                        strRelations.AppendLine("           " + strObjectName + "BO " + strObjectName.Substring(0, 1).ToLower() + strObjectName.Substring(1) + "Bo = " + strObjectName + "BLL.TryGetByIP(new " + strObjectName + "IP() { " + strPK + " = " + strPK + ", LocId = locId });");
                        strRelations.AppendLine("           if (" + strObjectName.Substring(0, 1).ToLower() + strObjectName.Substring(1) + "Bo != null)");
                        strRelations.AppendLine("               return " + strObjectName.Substring(0, 1).ToLower() + strObjectName.Substring(1) + "Bo;");
                        strRelations.AppendLine("           ");
                        strRelations.AppendLine("           // If no localized info was found for the given locId, try to find one based on localization priority");
                        strRelations.AppendLine("           List<LocBO> lstLoc = LocBLL.GetListByIP(new LocIP(), LocDAL.SortOrderType.Priority_ASC);");
                        strRelations.AppendLine("           if (lstLoc.Count > 0)");
                        strRelations.AppendLine("               " + strObjectName.Substring(0, 1).ToLower() + strObjectName.Substring(1) + "Bo = " + strObjectName + "BLL.TryGetByIP(new " + strObjectName + "IP() { " + strPK + " = " + strPK + ", LocId = lstLoc[0].LocId });");
                        strRelations.AppendLine("           ");
                        strRelations.AppendLine("           return " + strObjectName.Substring(0, 1).ToLower() + strObjectName.Substring(1) + "Bo;");
                        strRelations.AppendLine("        }");
                   }
                }
            }

            return strRelations.ToString();
        }
    }
}
